﻿namespace HashSum
{
    using System.Drawing;
    using System.IO;
    using System.Security.Cryptography;
    using ImageDupFinder.PluginCore;

    /// <summary>
    /// Class representing md5 hashsum feature. 
    /// </summary>
    public class MD5Sum : IFeature
    {
        /// <summary>
        /// feature name
        /// </summary>
        public const string FeatureName = "hash (md5)";

        /// <summary>
        /// image hash
        /// </summary>
        private readonly byte[] hash;

        /// <summary>
        /// feature calculated state.
        /// </summary>
        private bool calculated = false;

        /// <summary>
        /// Initializes a new instance of the MD5Sum class.
        /// </summary>
        /// <param name="image">Image used in feature extraction</param>
        public MD5Sum(Bitmap image)
        {
            byte[] byteArray = this.ImageToByte2(image);
            using (MD5CryptoServiceProvider pr = new MD5CryptoServiceProvider())
            {
                this.hash = pr.ComputeHash(byteArray);
            }

            this.calculated = true;
        }

        /// <summary>
        /// Gets image hash
        /// </summary>
        public byte[] Hash
        {
            get { return this.hash; }
        }

        /// <summary>
        /// Gets a value indicating whether feature is calculated.
        /// </summary>
        public bool IsCalculated
        {
            get { return this.calculated; }
        }

        /// <summary>
        /// Gets feature name.
        /// </summary>
        public string Name
        {
            get { return FeatureName; }
        }

        /// <summary>
        /// Calculate similarity between two MD5sum features.
        /// </summary>
        /// <param name="other">Second feature used in calcualtion</param>
        /// <returns>Similarity in range 0 - 1, where 0 means "identical" and 1 means "completly different"</returns>
        public float GetSimilarity(IFeature other)
        {
            if (!(other is MD5Sum) || !other.IsCalculated || !this.IsCalculated)
            {
                return 1.0f;
            }

            MD5Sum fr = other as MD5Sum;
            if (this.hash.Length != fr.hash.Length)
            {
                return 1.0f;
            }

            for (int i = 0; i < this.hash.Length; i++)
                if (this.hash[i] != fr.hash[i])
                {
                    return 1.0f;
                }

            return 0;
        }

        /// <summary>
        /// Converts bitmap to byte array.
        /// </summary>
        /// <param name="img">image to convert</param>
        /// <returns>image as byte array</returns>
        private byte[] ImageToByte2(Bitmap img)
        {
            byte[] byteArray = new byte[0];
            using (MemoryStream stream = new MemoryStream())
            {
                img.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);
                stream.Close();
                byteArray = stream.ToArray();
            }

            return byteArray;
        }
    }
}
